from flask import Blueprint, request, jsonify, current_app, url_for, send_from_directory
import json
import os
import uuid
from werkzeug.utils import secure_filename
from app.services.network_service import NetworkService
from app.services.recording_service import RecordingService
from app.services.storage_service import StorageService
from app.utils.audio import validate_audio_format, validate_word_segments
from app import cache, csrf
import datetime
import tempfile
import shutil
import random
import time

api_bp = Blueprint('api', __name__, url_prefix='/api')

# 明确禁用CSRF保护
@csrf.exempt
@api_bp.route('/upload', methods=['POST'])
def upload():
    """处理音频上传和标记数据"""
    try:
        current_app.logger.info("接收到上传请求")
        
        # 记录请求内容
        current_app.logger.info(f"请求表单数据键: {list(request.form.keys())}")
        current_app.logger.info(f"请求文件数据键: {list(request.files.keys())}")
        current_app.logger.info(f"请求内容类型: {request.content_type}")
        
        # 检查请求中是否包含音频文件
        if 'audio' not in request.files:
            current_app.logger.error("请求中没有音频文件")
            return jsonify({"status": "error", "message": "请求无效", "detail": "没有提供音频文件"}), 400
        
        audio_file = request.files['audio']
        
        # 检查文件名是否为空
        if audio_file.filename == '':
            current_app.logger.error("音频文件名为空")
            return jsonify({"status": "error", "message": "请求无效", "detail": "音频文件名为空"}), 400
        
        # 验证音频格式
        if not validate_audio_format(audio_file):
            current_app.logger.error(f"不支持的音频格式: {audio_file.filename}")
            return jsonify({"status": "error", "message": "不支持的音频格式", "detail": "仅支持WAV、MP3和OGG格式"}), 400
        
        # 获取元数据
        if 'metadata' in request.form:
            try:
                metadata = json.loads(request.form['metadata'])
                word_segments = metadata.get('wordSegments', [])
                
                # 检查是否是prolific平台且用户是否已完成
                if metadata.get('platform') == 'prolific':
                    prolific_pid = metadata.get('prolificPID')
                    study_id = metadata.get('prolificStudyID')
                    
                    if prolific_pid:
                        # 检查完成记录
                        completions_file = os.path.join('logs/completions', 'completions.json')
                        if os.path.exists(completions_file):
                            try:
                                with open(completions_file, 'r') as f:
                                    completions_data = json.load(f)
                                    if prolific_pid in completions_data:
                                        current_app.logger.warning(f"用户 {prolific_pid} 已完成任务，拒绝重复提交")
                                        return jsonify({"status": "error", "message": "您已完成此任务，不能重复提交"}), 403
                            except Exception as e:
                                current_app.logger.error(f"读取完成记录出错: {str(e)}")
                
                # 验证元数据格式
                if not validate_word_segments(word_segments):
                    current_app.logger.error("单词分段数据格式无效")
                    return jsonify({"status": "error", "message": "单词分段数据格式无效"}), 400
            except json.JSONDecodeError:
                current_app.logger.error("元数据不是有效的JSON")
                return jsonify({"status": "error", "message": "元数据不是有效的JSON"}), 400
        else:
            current_app.logger.error("请求中没有元数据")
            return jsonify({"status": "error", "message": "请求无效", "detail": "没有提供元数据"}), 400
        
        # 获取客户端真实IP地址
        client_ip = request.remote_addr
        # 尝试从代理头获取真实IP
        if request.headers.get('X-Forwarded-For'):
            client_ip = request.headers.get('X-Forwarded-For').split(',')[0].strip()
        elif request.headers.get('X-Real-IP'):
            client_ip = request.headers.get('X-Real-IP')
        
        # 收集用户信息
        user_info = {
            # 网络信息
            'ip_address': client_ip,
            'forwarded_for': request.headers.get('X-Forwarded-For'),
            'real_ip': request.headers.get('X-Real-IP'),
            'proxy_info': request.headers.get('Via'),
            'user_agent': request.user_agent.string,
            'browser': request.user_agent.browser,
            'browser_version': request.user_agent.version,
            'platform': request.user_agent.platform,
            'language': request.accept_languages.best if request.accept_languages else None,
            
            # 设备类型信息 (从User-Agent推断)
            'is_mobile': request.user_agent.platform in ['android', 'iphone', 'ipad'],
            'is_tablet': 'ipad' in request.user_agent.string.lower() or 'tablet' in request.user_agent.string.lower(),
            'is_pc': not (request.user_agent.platform in ['android', 'iphone', 'ipad'] or 'tablet' in request.user_agent.string.lower())
        }
        
        # 添加用户信息到元数据
        metadata['userInfo'] = user_info
        
        # 确保平台信息被正确保存到用户信息中
        if 'platform' in metadata:
            user_info['platform'] = metadata['platform']
        
        # 处理prolific特有信息
        if metadata.get('platform') == 'prolific':
            prolific_pid = metadata.get('prolificPID')
            if prolific_pid:
                user_info['prolificPID'] = prolific_pid
                user_info['prolificStudyID'] = metadata.get('prolificStudyID', '')
                user_info['prolificSessionID'] = metadata.get('prolificSessionID', '')
        
        # 保存录音
        recording_service = RecordingService()
        result = recording_service.save_recording(audio_file, word_segments, user_info)
        
        if result.get('status') == 'success':
            return jsonify({
                "status": "success",
                "message": "上传成功",
                "recording": result.get('recording')
            }), 201
        else:
            return jsonify({
                "status": "error",
                "message": result.get('message', '上传失败')
            }), 500
    
    except Exception as e:
        current_app.logger.error(f"处理上传请求时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

@api_bp.route('/recordings', methods=['GET'])
def get_recordings():
    """获取录音列表"""
    try:
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 10, type=int)
        
        result = RecordingService.get_recordings(page=page, per_page=per_page)
        
        return jsonify({
            "status": "success",
            "recordings": result.get('recordings', []),
            "pagination": {
                "total": result.get('total', 0),
                "page": result.get('page', 1),
                "per_page": result.get('per_page', 10),
                "pages": result.get('pages', 0)
            }
        })
    
    except Exception as e:
        current_app.logger.error(f"获取录音列表时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

@api_bp.route('/recordings/<recording_id>', methods=['GET'])
def get_recording(recording_id):
    """获取单个录音详情"""
    try:
        recording = RecordingService.get_recording(recording_id)
        
        if not recording:
            return jsonify({"status": "error", "message": "录音不存在"}), 404
            
        return jsonify({
            "status": "success",
            "recording": recording
        })
    
    except Exception as e:
        current_app.logger.error(f"获取录音详情时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

@api_bp.route('/recordings/<recording_id>', methods=['DELETE'])
def delete_recording(recording_id):
    """删除录音"""
    try:
        success = RecordingService.delete_recording(recording_id)
        
        if not success:
            return jsonify({"status": "error", "message": "录音不存在或删除失败"}), 404
            
        return jsonify({
            "status": "success",
            "message": "录音已删除"
        })
    
    except Exception as e:
        current_app.logger.error(f"删除录音时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

@api_bp.route('/network', methods=['GET'])
def get_network_info():
    """获取网络信息"""
    try:
        refresh = request.args.get('refresh', '').lower() == 'true'
        network_info = NetworkService.get_network_info(refresh=refresh)
        
        return jsonify({
            "status": "success",
            "network_info": network_info
        })
    
    except Exception as e:
        current_app.logger.error(f"获取网络信息时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

@api_bp.route('/qrcode', methods=['GET'])
def get_qr_code():
    """获取二维码URL"""
    try:
        qr_code_url = NetworkService.get_qr_code_url()
        
        if not qr_code_url:
            return jsonify({"status": "error", "message": "无法生成二维码"}), 400
            
        return jsonify({
            "status": "success",
            "qr_code_url": qr_code_url
        })
    
    except Exception as e:
        current_app.logger.error(f"获取二维码URL时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

@api_bp.route('/storage', methods=['GET'])
def get_storage_stats():
    """获取存储统计信息"""
    try:
        storage_stats = StorageService.get_storage_stats()
        
        return jsonify({
            "status": "success",
            "storage_stats": storage_stats
        })
    
    except Exception as e:
        current_app.logger.error(f"获取存储统计信息时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

@api_bp.route('/cleanup', methods=['POST'])
def cleanup_orphaned_files():
    """清理孤立文件"""
    try:
        result = StorageService.cleanup_orphaned_files()
        
        if result.get('status') == 'error':
            return jsonify({"status": "error", "message": result.get('message')}), 400
            
        return jsonify({
            "status": "success",
            "message": "清理完成",
            "deleted_count": result.get('deleted_count', 0),
            "deleted_size": result.get('deleted_size', 0),
            "deleted_size_readable": result.get('deleted_size_readable', '0 B')
        })
    
    except Exception as e:
        current_app.logger.error(f"清理孤立文件时发生错误: {str(e)}")
        return jsonify({"status": "error", "message": "服务器内部错误", "detail": str(e)}), 500

# 临时会话存储
session_storage = {}

@csrf.exempt
@api_bp.route('/upload_chunk', methods=['POST'])
def upload_chunk():
    """处理音频块上传"""
    try:
        # 检查是否有音频文件
        if 'audio_chunk' not in request.files:
            return jsonify({
                'status': 'error',
                'message': '没有找到音频文件'
            }), 400
        
        audio_chunk = request.files['audio_chunk']
        if not audio_chunk or not audio_chunk.filename:
            return jsonify({
                'status': 'error',
                'message': '无效的音频文件'
            }), 400
        
        # 获取会话ID
        session_id = request.form.get('session_id')
        if not session_id:
            session_id = f"session_{int(time.time())}_{uuid.uuid4().hex[:8]}"
        
        # 获取是否是最终块
        is_final = request.form.get('is_final', 'false').lower() == 'true'
        
        # 获取当前块索引
        chunk_index = request.form.get('chunk_index', '0')
        
        # 获取当前词段信息
        current_segment = None
        if 'current_segment' in request.form:
            current_segment = json.loads(request.form['current_segment'])
        
        # 获取元数据
        metadata = None
        if 'metadata' in request.form:
            metadata = json.loads(request.form['metadata'])
        
        # 保存音频块
        recording_service = RecordingService()
        chunk_result = recording_service.save_chunk(
            audio_chunk=audio_chunk,
            session_id=session_id,
            chunk_index=chunk_index,
            is_final=is_final
        )
        
        if chunk_result['status'] == 'error':
            return jsonify(chunk_result), 500
        
        # 如果是最终块，保存元数据
        if is_final and metadata:
            metadata_result = recording_service.save_metadata(
                metadata=metadata,
                session_id=session_id
            )
            
            if metadata_result['status'] == 'error':
                return jsonify(metadata_result), 500
        
        # 清理旧文件
        recording_service.cleanup_old_files()
        
        return jsonify({
            'status': 'success',
            'message': '音频块上传成功',
            'session_id': session_id,
            'is_final': is_final
        })
        
    except Exception as e:
        logger.error(f"处理音频块上传失败: {str(e)}")
        return jsonify({
            'status': 'error',
            'message': f'处理音频块上传失败: {str(e)}'
        }), 500

def merge_audio_chunks(chunks, output_path):
    """
    合并多个音频块为一个WAV文件
    
    Args:
        chunks: 包含路径的音频块列表
        output_path: 输出文件路径
    
    Returns:
        bool: 是否成功
    """
    try:
        # 按时间戳排序块
        sorted_chunks = sorted(chunks, key=lambda x: x.get('timestamp', ''))
        chunk_count = len(sorted_chunks)
        current_app.logger.info(f"准备合并 {chunk_count} 个音频块")
        
        # 过滤出实际存在的块
        existing_chunks = []
        for chunk in sorted_chunks:
            if os.path.exists(chunk['path']):
                existing_chunks.append(chunk)
                current_app.logger.info(f"块存在: {chunk['path']}")
            else:
                current_app.logger.warning(f"跳过不存在的块: {chunk['path']}")
        
        # 更新块数量
        chunk_count = len(existing_chunks)
        current_app.logger.info(f"过滤后剩余 {chunk_count} 个可用块")
        
        # 如果没有块，直接返回失败
        if not existing_chunks:
            current_app.logger.error("没有可用的音频块可合并")
            return False
        
        # 创建临时目录用于合并过程，确保不影响原始文件
        temp_merge_dir = tempfile.mkdtemp(prefix="wav_merge_")
        current_app.logger.info(f"创建临时合并目录: {temp_merge_dir}")
        
        # 复制第一个块作为基础文件
        first_chunk_path = existing_chunks[0]['path']
        base_file_path = os.path.join(temp_merge_dir, "base_file.wav")
        current_app.logger.info(f"复制第一个块 {first_chunk_path} 到 {base_file_path}")
        
        try:
            shutil.copy(first_chunk_path, base_file_path)
            current_app.logger.info(f"成功复制第一个块")
        except Exception as e:
            current_app.logger.error(f"复制第一个块失败: {str(e)}")
            # 尝试创建一个空的WAV文件
            try:
                from scipy.io import wavfile
                import numpy as np
                # 创建一个1秒的空白音频
                sample_rate = 16000
                data = np.zeros(sample_rate, dtype=np.int16)
                wavfile.write(base_file_path, sample_rate, data)
                current_app.logger.info(f"创建了空白WAV文件: {base_file_path}")
            except Exception as sub_e:
                current_app.logger.error(f"创建空白WAV文件失败: {str(sub_e)}")
                shutil.rmtree(temp_merge_dir, ignore_errors=True)
                return False
        
        # 确保基础文件存在
        if not os.path.exists(base_file_path):
            current_app.logger.error(f"无法创建基础文件: {base_file_path}")
            shutil.rmtree(temp_merge_dir, ignore_errors=True)
            return False
        
        # 如果只有一个块，直接复制到输出路径
        if len(existing_chunks) == 1:
            current_app.logger.info("只有一个块，直接复制到输出路径")
            try:
                shutil.copy(base_file_path, output_path)
                current_app.logger.info(f"单块音频复制到: {output_path}")
                # 清理临时目录
                shutil.rmtree(temp_merge_dir, ignore_errors=True)
                return True
            except Exception as e:
                current_app.logger.error(f"复制单块文件失败: {str(e)}")
                shutil.rmtree(temp_merge_dir, ignore_errors=True)
                return False
        
        # 获取第一个WAV文件的头大小（通常为44字节）
        header_size = 44
        current_app.logger.info(f"使用WAV头大小: {header_size}字节")
        
        # 从第二个块开始，追加数据（跳过WAV头）
        merged_file_path = os.path.join(temp_merge_dir, "merged_file.wav")
        try:
            # 首先复制基础文件
            shutil.copy(base_file_path, merged_file_path)
            current_app.logger.info(f"创建合并文件: {merged_file_path}")
            
            with open(merged_file_path, 'ab') as output_file:
                for i in range(1, len(existing_chunks)):
                    chunk_path = existing_chunks[i]['path']
                    current_app.logger.info(f"添加块 {i+1}/{chunk_count}: {chunk_path}")
                    
                    try:
                        with open(chunk_path, 'rb') as chunk_file:
                            # 跳过WAV头
                            chunk_file.seek(header_size)
                            # 读取并追加数据部分
                            data = chunk_file.read()
                            current_app.logger.info(f"从块 {i+1} 读取了 {len(data)} 字节的数据")
                            output_file.write(data)
                    except Exception as e:
                        current_app.logger.error(f"处理块 {i+1} 时出错: {str(e)}")
                        # 继续处理其他块，而不是直接返回失败
                        current_app.logger.warning(f"跳过有问题的块 {i+1} 并继续")
                        continue
        except Exception as e:
            current_app.logger.error(f"合并块时出错: {str(e)}")
            shutil.rmtree(temp_merge_dir, ignore_errors=True)
            return False
        
        # 修复最终WAV文件的头部大小信息
        try:
            update_wav_header_size(merged_file_path)
            current_app.logger.info("已更新WAV头部大小信息")
        except Exception as e:
            current_app.logger.error(f"更新WAV头部失败: {str(e)}")
            # 不要因为更新头部失败而返回失败，继续处理
            current_app.logger.warning("虽然更新WAV头部失败，但将继续处理")
        
        # 检查最终文件大小
        final_size = os.path.getsize(merged_file_path)
        current_app.logger.info(f"最终合并文件大小: {final_size} 字节")
        
        # 复制最终文件到输出路径
        try:
            shutil.copy(merged_file_path, output_path)
            current_app.logger.info(f"复制最终合并文件到: {output_path}")
        except Exception as e:
            current_app.logger.error(f"复制最终合并文件失败: {str(e)}")
            shutil.rmtree(temp_merge_dir, ignore_errors=True)
            return False
        
        # 清理临时目录
        try:
            shutil.rmtree(temp_merge_dir, ignore_errors=True)
            current_app.logger.info(f"清理临时合并目录: {temp_merge_dir}")
        except Exception as e:
            current_app.logger.warning(f"清理临时合并目录失败: {str(e)}")
        
        # 最终检查输出文件是否存在
        if os.path.exists(output_path):
            current_app.logger.info(f"合并完成，输出文件: {output_path}")
            return True
        else:
            current_app.logger.error(f"合并失败，输出文件不存在: {output_path}")
            return False
    except Exception as e:
        current_app.logger.error(f"合并音频块失败: {str(e)}")
        current_app.logger.exception("详细错误信息:")
        return False

def update_wav_header_size(wav_path):
    """
    更新WAV文件头部的文件大小信息
    
    Args:
        wav_path: WAV文件路径
    """
    try:
        # 获取文件大小
        file_size = os.path.getsize(wav_path)
        
        # 打开文件并更新头部信息
        with open(wav_path, 'r+b') as f:
            # 更新RIFF块大小 (文件大小 - 8)
            f.seek(4)
            f.write((file_size - 8).to_bytes(4, byteorder='little'))
            
            # 更新data块大小 (文件大小 - 44)
            f.seek(40)
            f.write((file_size - 44).to_bytes(4, byteorder='little'))
    except Exception as e:
        current_app.logger.error(f"更新WAV头部失败: {str(e)}") 